/** 
 * LoggerConfigurationBuilder.java
 * @author Lex
 * 2007-9-1 ����03:47:10
 */
package org.lex.utils.logging;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.SimpleFormatter;

public class LoggerConfigurationBuilder {
	public static final String CONSOLE_HANDLER = ConsoleHandler.class.getName();
	public static final String FILE_HANDLER = FileHandler.class.getName();
	public static final String SEPARATOR = ", ";
	public static final String FORMATTER = SimpleFormatter.class.getName();
	public static final String TINY_FORMATTER = TinyFormatter.class.getName();
	public static final String LEX_FORMATTER = MyFormatter.class.getName();
	// ====================================
	private Properties p = null;
	// ====================================
	private List<String> handlers = null;
	private Level defaultLevel = Level.INFO;

	/**
	 * convenient method for debugging
	 */
	public static void setDebugMode() {
		setDebugMode(Level.ALL, FORMATTER);
	}

	public static void setDebugMode(Level level) {
		setDebugMode(level, FORMATTER);
	}

	public static void setDebugMode(Level level, Class<? extends Formatter> formatter) {
		setDebugMode(level, formatter.getName());
	}

	public static void setDebugMode(Level level, String formatter) {
		LoggerConfigurationBuilder b = new LoggerConfigurationBuilder();
		// b.loadDefault();
		b.setDefaultLevel(level);
		b.setHandlerLevel(CONSOLE_HANDLER, level);
		// b.setHandlerLevel( FILE_HANDLER, level );
		b.setHandlerFormatter(CONSOLE_HANDLER, formatter);
		// b.setHandlerFormatter( FILE_HANDLER, formatter );
		try {
			b.save(System.out, "test");
		} catch (IOException e) {
			e.printStackTrace();
		}
		// b.updateLogManager();
	}

	public static void testMethod(String logger) {
		Level level = Level.ALL;
		LoggerConfigurationBuilder b = new LoggerConfigurationBuilder();
		b.setDefaultLevel(level);
		b.setLoggerLevel(logger, level);
		b.setLoggerHandlers(logger, CONSOLE_HANDLER);
		b.setHandlerLevel(CONSOLE_HANDLER, level);
		b.setHandlerFormatter(CONSOLE_HANDLER, TINY_FORMATTER);
		b.updateLogManager();
	}

	// constructor
	public LoggerConfigurationBuilder() {
		p = new Properties();
		this.handlers = new ArrayList<String>(2);
		// ==========================================
		this.setDefaultLevel(defaultLevel);
	}

	// ===================== set default propertis =====================
	public LoggerConfigurationBuilder setDefaultLevel(Level level) {
		p.setProperty(".level", level.getName());
		return this;
	}

	public LoggerConfigurationBuilder addDefaultHandlers(Class<? extends Handler>... handlers) {
		if (null == handlers || 0 == handlers.length)
			return this;
		ArrayList<String> l = new ArrayList<String>(handlers.length);
		for (Class<? extends Handler> c : handlers)
			if (null != c)
				l.add(c.getName());
		return this.addDefaultHandlers(l.toArray(new String[0]));
	}

	public LoggerConfigurationBuilder addDefaultHandlers(String... handlerClassNames) {
		if (null == handlerClassNames || 0 == handlerClassNames.length)
			return this;
		// first add handler's class name to list
		for (String h : handlerClassNames)
			if (null != h)
				this.handlers.add(h);
		// then set the ".handler" property
		StringBuilder b = new StringBuilder();
		for (Iterator<String> i = handlers.iterator(); i.hasNext();) {
			b.append(i.next());
			if (true == i.hasNext())
				b.append(SEPARATOR);
		}
		p.setProperty("handlers", b.toString());
		return this;
	}

	// XXX maybe we should load the DEFAULTS from jre/logging.properties file
	public LoggerConfigurationBuilder loadDefault() {
		this.setDefaultLevel(defaultLevel);
		this.addDefaultHandlers("java.util.logging.ConsoleHandler");
		// =====================================================
		p.setProperty("java.util.logging.FileHandler.pattern", "%h/java%u.log");
		p.setProperty("java.util.logging.FileHandler.limit", "5000");
		p.setProperty("java.util.logging.FileHandler.count", "1");
		p.setProperty("java.util.logging.FileHandler.formatter", "java.util.logging.XMLFormatter");
		p.setProperty("java.util.logging.ConsoleHandler.level", "INFO");
		p.setProperty("java.util.logging.ConsoleHandler.formatter", "java.util.logging.SimpleFormatter");
		// ======================================================
		return this;
	}

	// =============== set logger's properties =======================
	public LoggerConfigurationBuilder setLoggerLevel(String logger, Level level) {
		return this.setProperty(logger, "level", level.getName());
	}

	public LoggerConfigurationBuilder setLoggerHandlers(String logger, String... handlerClassNames) {
		if (null == handlerClassNames || 0 == handlerClassNames.length)
			return this;
		StringBuilder b = new StringBuilder();
		for (int i = 0; i < handlerClassNames.length; i++) {
			String s = handlerClassNames[i];
			if (null != s)
				b.append(s);
			if (i < handlerClassNames.length - 1)
				b.append(SEPARATOR);
		}
		return this.setProperty(logger, "handlers", b.toString());
	}

	public LoggerConfigurationBuilder setLoggerUseParentHandlers(String logger, boolean useParentHandlers) {
		return this.setProperty(logger, "useParentHandlers", Boolean.toString(useParentHandlers));
	}

	// =============== set handler's propertoies ========================
	public LoggerConfigurationBuilder setHandlerLevel(String handler, Level level) {
		return this.setProperty(handler, "level", level.getName());
	}

	public LoggerConfigurationBuilder setHandlerFormatter(String handler, String formatter) {
		return this.setProperty(handler, "formatter", formatter);
	}

	public LoggerConfigurationBuilder setHandlerFormatter(String handler, Class<? extends Formatter> formatter) {
		return this.setProperty(handler, "formatter", formatter.getName());
	}

	public LoggerConfigurationBuilder setProperty(String key, String property, String value) {
		p.setProperty(key + "." + property, value);
		return this;
	}

	// =============================================
	public synchronized void updateLogManager() {
		try {
			LogManager.getLogManager().readConfiguration(getInputStream());
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public synchronized InputStream getInputStream() {
		ByteArrayOutputStream bas = new ByteArrayOutputStream();
		p.list(new PrintStream(bas));
		return new ByteArrayInputStream(bas.toByteArray());
	}

	public synchronized void save(OutputStream out, String comments) throws IOException {
		p.store(out, comments);
	}

	public synchronized void save(Writer writer, String comments) throws IOException {
		p.store(writer, comments);
	}

	public LoggerConfigurationBuilder load(File file) throws IOException {
		p.load(new FileInputStream(file));
		return this;
	}

	public LoggerConfigurationBuilder load(InputStream in) throws IOException {
		p.load(in);
		return this;
	}

	public LoggerConfigurationBuilder load(Reader reader) throws IOException {
		p.load(reader);
		return this;
	}

	public LoggerConfigurationBuilder loadFromXML(InputStream in) throws IOException {
		p.loadFromXML(in);
		return this;
	}

	public LoggerConfigurationBuilder clear() {
		p.clear();
		return this;
	}
}
